﻿using System.Runtime.Serialization;
using System.Collections.Generic;
using System;
using System.Linq;


namespace ScrumFactory {

    public enum ProjectStatus : short {        
        PROPOSAL_CREATION,        
        PROPOSAL_APPROVED,
        PROPOSAL_REJECTED,
        PROJECT_STARTED,
        PROJECT_DONE,   
        PROJECT_SUPPORT             
    }

    public enum ProjectTypes : short {
        NORMAL_PROJECT = 0,
        SUPPORT_PROJECT = 20
    }

    /// <summary>
    /// POCO object for the project.
    /// </summary>
    [DataContract]
    public class Project {

    
        /// <summary>
        /// Gets or sets the project Unique id.
        /// </summary>
        /// <value>The project Unique id.</value>
        [DataMember]
        public string ProjectUId { get; set; }

        /// <summary>
        /// Gets or sets the name of the project.
        /// </summary>
        /// <value>The name of the project.</value>
        [DataMember]
        public string ProjectName { get; set; }

        /// <summary>
        /// Gets or sets the project number.
        /// </summary>
        /// <value>The project number.</value>
        [DataMember]
        public int ProjectNumber { get; set; }

        [DataMember]
        public string ClientName { get; set; }

        [DataMember]
        public string Description { get; set; }

        [DataMember]
        public short Status { get; set; }

        [DataMember]
        public System.DateTime CreateDate { get; set; }

        [DataMember]
        public String CreateBy { get; set; }

        [DataMember]
        public String Platform { get; set; }


        [DataMember]
        public DateTime? StartDate { get; set; }


        [DataMember]
        public DateTime? EndDate { get; set; }

        [DataMember]
        public string CodeRepositoryPath { get; set; }

        [DataMember]
        public string DocRepositoryPath { get; set; }

        [DataMember]
        public List<Role> Roles { get; set; }

        [DataMember]
        public bool AnyoneCanJoin { get; set; }

        [DataMember]
        public short ProjectType { get; set; }

        [DataMember]
        public List<Sprint> Sprints { get; set; }

        // one day i will implement it
        private ProjectOptions projectOptions;
        public ProjectOptions ProjectOptions {
            get {
                if (projectOptions == null)
                    projectOptions = new ProjectOptions();
                return projectOptions;
            }
            set {
                projectOptions = value;
            }
        }

        [DataMember]
        public bool IsSuspended { get; set; }

        [DataMember]
        public List<ProjectMembership> Memberships { get; set; }

        /// <summary>
        /// Define the number of days ripping of the saturdays and sundays.
        /// </summary>
        /// <returns>The number of work days</returns>
        public static int CalcWorkDayCount(DateTime start, DateTime end) {
            DateTime day = start.Date;
            int dayCount = 0;
            while (day <= end.Date) {
                if (IsWorkDay(day))
                    dayCount++;
                day = day.AddDays(1);
            }
            return dayCount;

        }
        /// <summary>
        /// Normal human beings do not work on weekends.
        /// Verify if the day is a work day.
        /// </summary>
        /// <param name="day"></param>
        /// <returns>True if the day is not a weekend</returns>
        public static bool IsWorkDay(DateTime day) {
            return (!DayOfWeek.Saturday.Equals(day.DayOfWeek) &&
                    !DayOfWeek.Sunday.Equals(day.DayOfWeek));
        }

                
        /// <summary>
        /// Gets the current sprint.
        /// If there is no current sprint, returns nulls.
        /// </summary>
        public Sprint CurrentSprint {
            get {
                if (Sprints == null || Sprints.Count == 0)
                    return null;

                if (!IsRunning)
                    return null;
                
                return SprintForDate(DateTime.Today);                
            }
        }

        public Sprint SprintForDate(DateTime date) {
            Sprint sprint = null;
            foreach (Sprint s in Sprints.OrderBy(i => i.SprintNumber)) {
                if (date >= s.StartDate.Date && date <= s.EndDate.Date) {
                    sprint = s;
                    break;
                }
            }
            return sprint;         
        }

        public bool IsRunning {
            get {
                return (Status == (short)ProjectStatus.PROJECT_STARTED || Status == (short)ProjectStatus.PROJECT_SUPPORT);
            }
        }

        /// <summary>
        /// Gets a current valid Sprint.
        /// If the first sprint has not yet started return the first.
        /// If the last sprint is over, return the last.
        /// </summary>
        public Sprint CurrentValidSprint {
            get {

                if (Sprints == null || Sprints.Count == 0)
                    return null;

                if (CurrentSprint != null)     
                    return CurrentSprint;

                Sprint firstSprint = Sprints.SingleOrDefault(s => s.SprintNumber == 1);
                if (!IsRunning)
                    return firstSprint;

                if (firstSprint.StartDate.Date > System.DateTime.Today)
                    return firstSprint;


                return Sprints.SingleOrDefault(s => s.SprintNumber == Sprints.Count);
            }
        }

        public bool HasPermission(string memberUId, PermissionSets permission) {
            if (Memberships == null)
                return false;
            return Memberships.Any(m => m.MemberUId == memberUId && m.IsActive==true && m.Role.PermissionSet == (short)permission);
        }
        

        /// <summary>
        /// Gets the current planning number.
        /// If the project has not started yet, returns zero (=proposal plan).        
        /// </summary>
        public int CurrentPlanningNumber {
            get {
                if (!IsRunning)
                    return 0;
                return CurrentValidSprint.SprintNumber;
            }
        }

        /// <summary>
        /// Gets the next sprint number.
        /// </summary>
        public int NextSprintNumber {
            get {
                if (Sprints == null)
                    throw new System.Exception("Project Sprints has not been loaded yet");
                return Sprints.Count + 1;
            }
        }
        
        public Sprint FirstSprint {
            get {
                if (Sprints == null || Sprints.Count == 0)
                    return null;
                return Sprints.SingleOrDefault(s => s.SprintNumber == 1);
            }
        }

        public Sprint LastSprint {
            get {
                if (Sprints == null || Sprints.Count == 0)
                    return null;

                return Sprints.SingleOrDefault(s => s.SprintNumber == Sprints.Max(s1 => s1.SprintNumber));
            }
        }

        public Role DefaultRole {
            get {
                if (Roles == null)
                    return null;
                return Roles.FirstOrDefault(r => r.IsDefaultRole == true);
            }
        }
       
        public bool IsTheSame(Project p) {
            if (p == null)
                return false;
            return 
                this.ProjectName == p.ProjectName &&
                this.ProjectNumber == p.ProjectNumber &&
                this.ClientName == p.ClientName &&
                this.CodeRepositoryPath == p.CodeRepositoryPath &&
                this.DocRepositoryPath == p.DocRepositoryPath &&
                this.Description == p.Description &&
                this.ProjectType == p.ProjectType &&
                this.AnyoneCanJoin == p.AnyoneCanJoin &&
                this.IsSuspended == p.IsSuspended;
        }


        ~Project() {
            System.Console.Out.WriteLine("---> project died here");
        }
        

    }

    


    public class ProjectOptions {

        public bool AddPlanAndDeliveryItemsToSprint { get; set; }
        public bool AutoAddSprints { get; set; }
        public int SprintLimitHours { get; set; }
        public int SprintLimitDays { get; set; }

        public ProjectOptions() {
            AddPlanAndDeliveryItemsToSprint = true;
            AutoAddSprints = false;
            SprintLimitHours = 160;
            SprintLimitDays = 15;

        }

    }

}
